package org.hawk.gwt.ppc.scanner.utils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.ListIterator;

import org.hawk.gwt.ppc.javacc.ParseException;
import org.hawk.gwt.ppc.utils.Name;

/**
 * Represents generic type variable for the {@link JavaTypeScope} or {@link JavaMethod}.
 * 
 * @author alex.bereznevatiy@gmail.com
 */
public class JavaTypeVariable extends JavaGenericTypeScope {
	private List<JavaTypeScope> bounds;
	private String name;

	JavaTypeVariable(String name, JavaTypeScope type, JavaNameResolver owner, List<String> bounds) throws IOException, ParseException {
		super(type, owner);
		this.name = name;
		this.bounds = getBounds(bounds);
	}

	private List<JavaTypeScope> getBounds(List<String> unresolvedBounds) throws IOException, ParseException {
		ListIterator<String> iterator = unresolvedBounds.listIterator();
		List<JavaTypeScope> result = new ArrayList<JavaTypeScope>(unresolvedBounds.size());
		while(iterator.hasNext()) {
			String typeName = getOwner().resolveType(iterator.next());
			String rawName = Name.getRawName(typeName);
			String params = Name.getTypeParameters(typeName);
			
			if (params.isEmpty()) {
				result.add(JavaClassDescriptorUtils.getClassDescriptor(rawName).getCompilationUnit());
			} else if (params.endsWith("[]")) {
				result.add(resolveArrayType(rawName, params));
			} else {
				JavaTypeScope type = JavaClassDescriptorUtils.getClassDescriptor(rawName).getCompilationUnit();
				result.add(new JavaParameterizedGenericTypeScope(type, getOwner(), params));
			}
		}
		return Collections.unmodifiableList(result);
	}

	private JavaTypeScope resolveArrayType(String type, String params) throws IOException, ParseException {
		params = params.substring(0, params.length() - 2);
		if (params.endsWith("[]")) {
			return new JavaArrayGenericTypeScope(resolveArrayType(type, params), getOwner());
		}
		JavaTypeScope component = new JavaParameterizedGenericTypeScope(
				JavaClassDescriptorUtils.getClassDescriptor(type).getCompilationUnit(), getOwner(), params);
		return new JavaArrayGenericTypeScope(component, getOwner());
	}

	/**
	 * @return bound type names for the variable
	 */
	public List<JavaTypeScope> getBounds() {
		return bounds;
	}

	/**
	 * @return variable name
	 */
	public String getName() {
		return name;
	}
}
